/*******************************************************************************
* Copyright (C) 2015 Spansion LLC. All Rights Reserved. 
*
* This software is owned and published by: 
* Spansion LLC, 915 DeGuigne Dr. Sunnyvale, CA  94088-3453 ("Spansion").
*
* BY DOWNLOADING, INSTALLING OR USING THIS SOFTWARE, YOU AGREE TO BE BOUND 
* BY ALL THE TERMS AND CONDITIONS OF THIS AGREEMENT.
*
* This software contains source code for use with Spansion 
* components. This software is licensed by Spansion to be adapted only 
* for use in systems utilizing Spansion components. Spansion shall not be 
* responsible for misuse or illegal use of this software for devices not 
* supported herein.  Spansion is providing this software "AS IS" and will 
* not be responsible for issues arising from incorrect user implementation 
* of the software.  
*
* SPANSION MAKES NO WARRANTY, EXPRESS OR IMPLIED, ARISING BY LAW OR OTHERWISE,
* REGARDING THE SOFTWARE (INCLUDING ANY ACOOMPANYING WRITTEN MATERIALS), 
* ITS PERFORMANCE OR SUITABILITY FOR YOUR INTENDED USE, INCLUDING, 
* WITHOUT LIMITATION, THE IMPLIED WARRANTY OF MERCHANTABILITY, THE IMPLIED 
* WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE OR USE, AND THE IMPLIED 
* WARRANTY OF NONINFRINGEMENT.  
* SPANSION SHALL HAVE NO LIABILITY (WHETHER IN CONTRACT, WARRANTY, TORT, 
* NEGLIGENCE OR OTHERWISE) FOR ANY DAMAGES WHATSOEVER (INCLUDING, WITHOUT 
* LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, 
* LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS) ARISING FROM USE OR 
* INABILITY TO USE THE SOFTWARE, INCLUDING, WITHOUT LIMITATION, ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOSS OF DATA, 
* SAVINGS OR PROFITS, 
* EVEN IF SPANSION HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 
* YOU ASSUME ALL RESPONSIBILITIES FOR SELECTION OF THE SOFTWARE TO ACHIEVE YOUR
* INTENDED RESULTS, AND FOR THE INSTALLATION OF, USE OF, AND RESULTS OBTAINED 
* FROM, THE SOFTWARE.  
*
* This software may be replicated in part or whole for the licensed use, 
* with the restriction that this Disclaimer and Copyright notice must be 
* included with each copy of this software, whether used in part or whole, 
* at all times.  
*****************************************************************************/
/** \file s25fl512s.c
 **
 ** \brief driver level
 **          Driver of SPANSION S25FL512S serial flash
 **
 ** \history
 **   - 2015-03-01 1.00 First version
 **
 *****************************************************************************/

#include "s25fl512s.h"
#include "HSSPI.h"

#define DEF_SETTING_RDDSEL		0	//Read Deselect Time = setting value + 1; i.e. 0->1 serial clock; 1->2 serial clocks; 2->3 serial clocks; 3->4 serial clocks
#define DEF_SETTING_WRDSEL		0	//Write/different command Deselect Time = setting value + 1; i.e. 0->1 serial clock; 1->2 serial clocks;......; 15->16 serial clocks
#define DEF_SETTING_SAFESYNC	0	//Safe Synchronization bit, 0->Normal operation and No safe resyn; 1->Add delay to ensure safe resyn
//#define DEF_SETTING_CDRS		1	//Clock Division Ratio Select, 1->Divide by 2 (i.e. 81Mhz/2=40.5MHz)
#define DEF_SETTING_CDRS		0	//Clock Division Ratio Select, 1->Divide by 2 (i.e. 81Mhz/1=81MHz) 

#define DEF_SETTING_SENDIAN_DM	0	//Direct Mode SPI Endian, 0->Big Endian; 1->Little Endian
#define DEF_SETTING_SENDIAN_CSM	1	//Command Sequencer Mode SPI Endian, 0->Big Endian; 1->Little Endian
#define DEF_SETTING_SDIR			0	//Shift Direction, 0->bit order in byte order from highest to lowest bit; 1->bit order in byte order from lowest to highest bit
#define DEF_SETTING_SS2CD		0	//slave-select to clock delay, ignore since it operates as mode 3
#define DEF_SETTING_SSPOL		0	//Slave Select Polarity, 0->active low; 1->active high for chip select pin
#define DEF_SETTING_RTM			1	//Use retimed clock for capturing the data, 0->without using timing compensated clock(Max. 20Mhz); 1->using timing compensated clock(Max. 40Mhz)

#define DEF_SETTING_FWIDTH		3	//FIFO Width setting, 0->8 bit, 1-> 16bit, 2->24 bit, 3->32 bit width for all of the TX-FIFO, RX-FIFO and shift registers

//Mode 3
#define DEF_SETTING_ACES		0	
#define DEF_SETTING_CPOL		0
#define DEF_SETTING_CPHA		0

//Command Sequencer Mode Setting
#define DEF_SETTING_MSEL		13	//0->8KB; 1->16KB; 2->32KB; 3->64KB; 4->128KB; 5->256KB; 6->512KB; 7->1MB; 8->2MB; 9->4MB; 10->8MB; 11->16MB; 12->32MB; 13->64MB; 14->128MB; 15->256MB
#define DEF_SETTING_SSEL0EN	1	//Enable Slave Select 0
#define DEF_SETTING_SSEL1EN	0	//Disable Slave Select 1
#define DEF_SETTING_SSEL2EN	0	//Disable Slave Select 2
#define DEF_SETTING_SSEL3EN	0	//Disable Slave Select 3
#define DEF_SETTING_MultiBitMode		2	//0->Single bit, 1->dual bits, 2->quad bits
#define DEF_SETTING_SRAM		0	//0->Read Only, 1->Enable Write Access

#define DEF_READ_COMMAND		0xEC//0xEC - Quad I/O Read(22 cycles overhead)  , 0x6C - Quad Output Read(40 cycles overhead)

#if (DEF_READ_COMMAND==0x6C)
//Quad Output Read (40 cycles overhead)
//Read Command Set
#define DEF_SETTING_RDCSDC0	0x6C06	//Command: 0x6C, CONT=0, Single Bit tx -> 8 Cycles
#define DEF_SETTING_RDCSDC1	0x0307	//Address[31:17], CONT=0, Single Bit tx -> 8 Cycles
#define DEF_SETTING_RDCSDC2	0x0207	//Address[23:16], CONT=0, Single Bit tx -> 8 Cycles
#define DEF_SETTING_RDCSDC3	0x0107	//Address[15:8], CONT=0, Single Bit tx -> 8 Cycles
#define DEF_SETTING_RDCSDC4	0x0007	//Address[7:0], CONT=0, Single Bit tx -> 8 Cycles
#define DEF_SETTING_RDCSDC5	0x0701	//End of List 
#define DEF_SETTING_RDCSDC6	0x0701	//Don't Care After End of List
#define DEF_SETTING_RDCSDC7	0x0701	//Don't Care After End of List

//Set LC=11 (<=50Mhz) to eliminate dummy cycle (it will be set during EnableQuadOutput)
#define DEF_LC	0x3

#elif (DEF_READ_COMMAND==0xEC)
//Quad I/O Read(22 cycles overhead) 
//Read Command Set
#define DEF_SETTING_RDCSDC0	0xEC06	//Command: 0xEC, CONT=0, Single Bit tx -> 8 Cycles
#define DEF_SETTING_RDCSDC1	0x0305	//Address[31:17], CONT=0, Quad Bit tx -> 2 Cycles
#define DEF_SETTING_RDCSDC2	0x0205	//Address[23:16], CONT=0, Quad Bit tx -> 2 Cycles
#define DEF_SETTING_RDCSDC3	0x0105	//Address[15:8], CONT=0, Quad Bit tx -> 2 Cycles
#define DEF_SETTING_RDCSDC4	0x0005	//Address[7:0], CONT=0, Quad Bit tx -> 2 Cycles
#define DEF_SETTING_RDCSDC5	0x0204	//Mode Bit = 0x2, CONT=0, Quad Bit tx ->2 Cycles
#define DEF_SETTING_RDCSDC6	0x0403	//Dummy Byte, CONT=0, Dual Bit tx -> 4 Cycles
#define DEF_SETTING_RDCSDC7	0x0701	//End of List

//Set LC=00 (<=80Mhz) to set the dummy cycle as 4 so that it can be definable (it will be set during EnableQuadOutput)
#define DEF_LC	0x0
#endif
/*******************************************************************************/
unsigned long FlashMFandID = 0;
/*******************************************************************************/
static void S25FL512S_Reset(unsigned char ui8Slave);
static unsigned long S25FL512S_Read_MF_ID(unsigned char ui8Slave);
/*******************************************************************************/
static void Delayus(volatile    unsigned int value)
{   
    volatile unsigned int i,j;
    for(i = 0; i < value; i++)
    {
        for(j = 0; j < 6; j++)
        {
              __wait_nop();
              __wait_nop();
        }   
    }
} 

void S25FL512S_initDirectMode()
{
    // disable module
	HSSPI_MCTRL_MEN = 0;
    Delayus(1);

	// wait for module to be disabled
	while( HSSPI_MCTRL_MES != 0 )
	{
	// do what has to be done while waiting
	}

	HSSPI_DMCFG = 0x00000003; // Byte Counter controlled
	
	/* configre communication control registers */
	HSSPI_PCC0_RDDSE = DEF_SETTING_RDDSEL;			//Read Deselect Time
	HSSPI_PCC0_WRDSE= DEF_SETTING_WRDSEL;			//Write/different command deselect time
	HSSPI_PCC0_SAFESYNC = DEF_SETTING_SAFESYNC;	//Safe resynchronization enable or not
	HSSPI_PCC0_CDRS = DEF_SETTING_CDRS; 			//Clock Division Ratio Select for SPI Clock
	HSSPI_PCC0_SENDIAN = DEF_SETTING_SENDIAN_DM; 	//Endian for data on serial interface during Direct Mode, i.e. API for direct mode is big endian
	HSSPI_PCC0_SDIR= DEF_SETTING_SDIR;				//Shift direction, the bit order in byte from highest/lowest bit
	HSSPI_PCC0_SS2CD= DEF_SETTING_SS2CD;			//Slave select to clock delay
	HSSPI_PCC0_SSPOL= DEF_SETTING_SSPOL;			//Slave select Polarity
	HSSPI_PCC0_RTM = DEF_SETTING_RTM;				//Use retimed clock for capturing the data or not
	HSSPI_PCC0_ACES= DEF_SETTING_ACES;				//Active Clock Edge
	HSSPI_PCC0_CPHA= DEF_SETTING_CPHA;				//Clock Polarity
	HSSPI_PCC0_CPOL= DEF_SETTING_CPOL;				//Clock Phase
	
	HSSPI_PCC1_RDDSE = DEF_SETTING_RDDSEL;			//Read Deselect Time
	HSSPI_PCC1_WRDSE= DEF_SETTING_WRDSEL;			//Write/different command deselect time
	HSSPI_PCC1_SAFESYNC = DEF_SETTING_SAFESYNC;	//Safe resynchronization enable or not
	HSSPI_PCC1_CDRS = DEF_SETTING_CDRS; 			//Clock Division Ratio Select for SPI Clock
	HSSPI_PCC1_SENDIAN = DEF_SETTING_SENDIAN_DM; 	//Endian for data on serial interface during Direct Mode, i.e. API for direct mode is big endian
	HSSPI_PCC1_SDIR= DEF_SETTING_SDIR;				//Shift direction, the bit order in byte from highest/lowest bit
	HSSPI_PCC1_SS2CD= DEF_SETTING_SS2CD;			//Slave select to clock delay
	HSSPI_PCC1_SSPOL= DEF_SETTING_SSPOL;			//Slave select Polarity
	HSSPI_PCC1_RTM = DEF_SETTING_RTM;				//Use retimed clock for capturing the data or not
	HSSPI_PCC1_ACES= DEF_SETTING_ACES;				//Active Clock Edge
	HSSPI_PCC1_CPHA= DEF_SETTING_CPHA;				//Clock Polarity
	HSSPI_PCC1_CPOL= DEF_SETTING_CPOL;				//Clock Phase

	HSSPI_PCC2_RDDSE = DEF_SETTING_RDDSEL;			//Read Deselect Time
	HSSPI_PCC2_WRDSE= DEF_SETTING_WRDSEL;			//Write/different command deselect time
	HSSPI_PCC2_SAFESYNC = DEF_SETTING_SAFESYNC;	//Safe resynchronization enable or not
	HSSPI_PCC2_CDRS = DEF_SETTING_CDRS; 			//Clock Division Ratio Select for SPI Clock
	HSSPI_PCC2_SENDIAN = DEF_SETTING_SENDIAN_DM; 	//Endian for data on serial interface during Direct Mode, i.e. API for direct mode is big endian
	HSSPI_PCC2_SDIR= DEF_SETTING_SDIR;				//Shift direction, the bit order in byte from highest/lowest bit
	HSSPI_PCC2_SS2CD= DEF_SETTING_SS2CD;			//Slave select to clock delay
	HSSPI_PCC2_SSPOL= DEF_SETTING_SSPOL;			//Slave select Polarity
	HSSPI_PCC2_RTM = DEF_SETTING_RTM;				//Use retimed clock for capturing the data or not
	HSSPI_PCC2_ACES= DEF_SETTING_ACES;				//Active Clock Edge
	HSSPI_PCC2_CPHA= DEF_SETTING_CPHA;				//Clock Polarity
	HSSPI_PCC2_CPOL= DEF_SETTING_CPOL;				//Clock Phase

	HSSPI_PCC3_RDDSE = DEF_SETTING_RDDSEL;			//Read Deselect Time
	HSSPI_PCC3_WRDSE= DEF_SETTING_WRDSEL;			//Write/different command deselect time
	HSSPI_PCC3_SAFESYNC = DEF_SETTING_SAFESYNC;	//Safe resynchronization enable or not
	HSSPI_PCC3_CDRS = DEF_SETTING_CDRS; 			//Clock Division Ratio Select for SPI Clock
	HSSPI_PCC3_SENDIAN = DEF_SETTING_SENDIAN_DM; 	//Endian for data on serial interface during Direct Mode, i.e. API for direct mode is big endian
	HSSPI_PCC3_SDIR= DEF_SETTING_SDIR;				//Shift direction, the bit order in byte from highest/lowest bit
	HSSPI_PCC3_SS2CD= DEF_SETTING_SS2CD;			//Slave select to clock delay
	HSSPI_PCC3_SSPOL= DEF_SETTING_SSPOL;			//Slave select Polarity
	HSSPI_PCC3_RTM = DEF_SETTING_RTM;				//Use retimed clock for capturing the data or not
	HSSPI_PCC3_ACES= DEF_SETTING_ACES;				//Active Clock Edge
	HSSPI_PCC3_CPHA= DEF_SETTING_CPHA;				//Clock Polarity
	HSSPI_PCC3_CPOL= DEF_SETTING_CPOL;				//Clock Phase
	
	// configre the FIFO width
	HSSPI_FIFOCFG_FWIDTH = DEF_SETTING_FWIDTH; // FIFO access width -> word
	
	/* setup direct mode */
	HSSPI_MCTRL_CSEN = 0;

	// enable module
	HSSPI_MCTRL_MEN = 1;
    Delayus(1);
    

	// wait for module to be enabled
	while( HSSPI_MCTRL_MES != 1 )
	{
	// do what has to be done while waiting
	}
    
    S25FL512S_Reset(0);
    FlashMFandID = S25FL512S_Read_MF_ID(0);
    FlashMFandID = FlashMFandID;
    if((FlashMFandID&0x00FF0000U) == 0x00EF0000U)
    {//winbond

    }
    else if((FlashMFandID&0x00FF0000U) == 0x009D0000U)
    {//ISSI

    }
    else if((FlashMFandID&0x00FF0000U) == 0x00C80000U)
    {//GD

    }
    else if((FlashMFandID&0x00FF0000U) == 0x00010000U)
    {//cypress

    }
    else
    {
    }
    
}

static void S25FL512S_Reset(unsigned char ui8Slave)
{
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;
    
	// select slave
	HSSPI_DMPSEL_PSEL = (ui8Slave & 0x03);
	
	// configure transport protocol (TX only, single bit)
	HSSPI_DMTRP_TRP = 0x00; // TRP[3..2] = b'00 (TX + RX), TRP[1..0] = b'00 (single bit)

	// set the byte TX byte count
	HSSPI_DMBCS_DMBCC = 12;

	// write command into TXFIFO
	HSSPI_TXFIFO0 = 0x00000000;
	HSSPI_TXFIFO1 = 0x00000000;
    HSSPI_TXFIFO2 = 0x00000000;
	// start transmission
	HSSPI_DMSTART_START = 1;
    
	while ( HSSPI_DMSTATUS_TXACTIVE == 1 || HSSPI_DMSTATUS_RXACTIVE == 1 )
	{
		// do what has to be done here
		WDTCPR1 = 0xa5;
	}
		
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;

    Delayus(1);
}

static unsigned long S25FL512S_Read_MF_ID(unsigned char ui8Slave)
{
	unsigned long dummy;
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;
    
	// select slave
	HSSPI_DMPSEL_PSEL = (ui8Slave & 0x03);
	
	// configure transport protocol (TX only, single bit)
	HSSPI_DMTRP_TRP = 0x00; // TRP[3..2] = b'00 (TX + RX), TRP[1..0] = b'00 (single bit)

	// set the byte TX byte count
	HSSPI_DMBCS_DMBCC = 4;

	// start transmission
	HSSPI_DMSTART_START = 1;
    
	// write command into TXFIFO
	HSSPI_TXFIFO0 = 0x9F000000;
			
	while ( HSSPI_DMSTATUS_TXACTIVE == 1 || HSSPI_DMSTATUS_RXACTIVE == 1 )
	{
		// do what has to be done here
		WDTCPR1 = 0xa5;
	}
		
	dummy = HSSPI_RXFIFO0; // dummy read on the RX FIFO
	
	return dummy;
}

unsigned char testflashbuf[100];
void S25FL512S_initCommandSequencerMode()
{
	// disable module
	HSSPI_MCTRL_MEN = 0;
    Delayus(1);
	// wait for module to be disabled
	while( HSSPI_MCTRL_MES != 0 )
	{
	// do what has to be done while waiting
	}
    
	HSSPI_DMCFG = 0x00000003; // Byte Counter controlled
	
	/* configre communication control registers */
	HSSPI_PCC0_RDDSE = DEF_SETTING_RDDSEL;			//Read Deselect Time
	HSSPI_PCC0_WRDSE= DEF_SETTING_WRDSEL;			//Write/different command deselect time
	HSSPI_PCC0_SAFESYNC = DEF_SETTING_SAFESYNC;	//Safe resynchronization enable or not
	HSSPI_PCC0_CDRS = DEF_SETTING_CDRS; 			//Clock Division Ratio Select for SPI Clock
	HSSPI_PCC0_SENDIAN = DEF_SETTING_SENDIAN_CSM; 	//Endian for data on serial interface during Command Sequencer Mode, i.e. GDC macro support Little endian
	HSSPI_PCC0_SDIR= DEF_SETTING_SDIR;				//Shift direction, the bit order in byte from highest/lowest bit
	HSSPI_PCC0_SS2CD= DEF_SETTING_SS2CD;			//Slave select to clock delay
	HSSPI_PCC0_SSPOL= DEF_SETTING_SSPOL;			//Slave select Polarity
	HSSPI_PCC0_RTM = DEF_SETTING_RTM;				//Use retimed clock for capturing the data or not
	HSSPI_PCC0_ACES= DEF_SETTING_ACES;				//Active Clock Edge
	HSSPI_PCC0_CPHA= DEF_SETTING_CPHA;				//Clock Polarity
	HSSPI_PCC0_CPOL= DEF_SETTING_CPOL;				//Clock Phase
	
	HSSPI_PCC1_RDDSE = DEF_SETTING_RDDSEL;			//Read Deselect Time
	HSSPI_PCC1_WRDSE= DEF_SETTING_WRDSEL;			//Write/different command deselect time
	HSSPI_PCC1_SAFESYNC = DEF_SETTING_SAFESYNC;	//Safe resynchronization enable or not
	HSSPI_PCC1_CDRS = DEF_SETTING_CDRS; 			//Clock Division Ratio Select for SPI Clock
	HSSPI_PCC1_SENDIAN = DEF_SETTING_SENDIAN_CSM; 	//Endian for data on serial interface during Command Sequencer Mode, i.e. GDC macro support Little endian
	HSSPI_PCC1_SDIR= DEF_SETTING_SDIR;				//Shift direction, the bit order in byte from highest/lowest bit
	HSSPI_PCC1_SS2CD= DEF_SETTING_SS2CD;			//Slave select to clock delay
	HSSPI_PCC1_SSPOL= DEF_SETTING_SSPOL;			//Slave select Polarity
	HSSPI_PCC1_RTM = DEF_SETTING_RTM;				//Use retimed clock for capturing the data or not
	HSSPI_PCC1_ACES= DEF_SETTING_ACES;				//Active Clock Edge
	HSSPI_PCC1_CPHA= DEF_SETTING_CPHA;				//Clock Polarity
	HSSPI_PCC1_CPOL= DEF_SETTING_CPOL;				//Clock Phase

	HSSPI_PCC2_RDDSE = DEF_SETTING_RDDSEL;			//Read Deselect Time
	HSSPI_PCC2_WRDSE= DEF_SETTING_WRDSEL;			//Write/different command deselect time
	HSSPI_PCC2_SAFESYNC = DEF_SETTING_SAFESYNC;	//Safe resynchronization enable or not
	HSSPI_PCC2_CDRS = DEF_SETTING_CDRS; 			//Clock Division Ratio Select for SPI Clock
	HSSPI_PCC2_SENDIAN = DEF_SETTING_SENDIAN_CSM; 	//Endian for data on serial interface during Command Sequencer Mode, i.e. GDC macro support Little endian
	HSSPI_PCC2_SDIR= DEF_SETTING_SDIR;				//Shift direction, the bit order in byte from highest/lowest bit
	HSSPI_PCC2_SS2CD= DEF_SETTING_SS2CD;			//Slave select to clock delay
	HSSPI_PCC2_SSPOL= DEF_SETTING_SSPOL;			//Slave select Polarity
	HSSPI_PCC2_RTM = DEF_SETTING_RTM;				//Use retimed clock for capturing the data or not
	HSSPI_PCC2_ACES= DEF_SETTING_ACES;				//Active Clock Edge
	HSSPI_PCC2_CPHA= DEF_SETTING_CPHA;				//Clock Polarity
	HSSPI_PCC2_CPOL= DEF_SETTING_CPOL;				//Clock Phase

	HSSPI_PCC3_RDDSE = DEF_SETTING_RDDSEL;			//Read Deselect Time
	HSSPI_PCC3_WRDSE= DEF_SETTING_WRDSEL;			//Write/different command deselect time
	HSSPI_PCC3_SAFESYNC = DEF_SETTING_SAFESYNC;	//Safe resynchronization enable or not
	HSSPI_PCC3_CDRS = DEF_SETTING_CDRS; 			//Clock Division Ratio Select for SPI Clock
	HSSPI_PCC3_SENDIAN = DEF_SETTING_SENDIAN_CSM; 	//Endian for data on serial interface during Command Sequencer Mode, i.e. GDC macro support Little endian
	HSSPI_PCC3_SDIR= DEF_SETTING_SDIR;				//Shift direction, the bit order in byte from highest/lowest bit
	HSSPI_PCC3_SS2CD= DEF_SETTING_SS2CD;			//Slave select to clock delay
	HSSPI_PCC3_SSPOL= DEF_SETTING_SSPOL;			//Slave select Polarity
	HSSPI_PCC3_RTM = DEF_SETTING_RTM;				//Use retimed clock for capturing the data or not
	HSSPI_PCC3_ACES= DEF_SETTING_ACES;				//Active Clock Edge
	HSSPI_PCC3_CPHA= DEF_SETTING_CPHA;				//Clock Polarity
	HSSPI_PCC3_CPOL= DEF_SETTING_CPOL;				//Clock Phase
	
	// configre the FIFO width
	HSSPI_FIFOCFG_FWIDTH = DEF_SETTING_FWIDTH; // FIFO access width -> word

	HSSPI_CSCFG_MSEL = DEF_SETTING_MSEL; // Size
	HSSPI_CSCFG_SSEL0EN = DEF_SETTING_SSEL0EN;
	HSSPI_CSCFG_SSEL1EN = DEF_SETTING_SSEL1EN;
	HSSPI_CSCFG_SSEL2EN = DEF_SETTING_SSEL2EN;
	HSSPI_CSCFG_SSEL3EN = DEF_SETTING_SSEL3EN;
	HSSPI_CSCFG_MBM = DEF_SETTING_MultiBitMode; // Quad/Dual/Single bit
	HSSPI_CSCFG_SRAM= DEF_SETTING_SRAM;

    if((FlashMFandID&0x00FF00FFU) == 0x00EF0018U)
    {//winbond w25q128
        //Set Read Command
    	HSSPI_RDCSDC0 = 0xEB06u;
    	HSSPI_RDCSDC1 = 0x0205u;
    	HSSPI_RDCSDC2 = 0x0105u;
    	HSSPI_RDCSDC3 = 0x0005u;
    	HSSPI_RDCSDC4 = 0x0204u;
    	HSSPI_RDCSDC5 = 0x0403u;
    	HSSPI_RDCSDC6 = 0x0701u;
    }
    else
    {
        //Set Read Command
    	HSSPI_RDCSDC0 = DEF_SETTING_RDCSDC0;
    	HSSPI_RDCSDC1 = DEF_SETTING_RDCSDC1;
    	HSSPI_RDCSDC2 = DEF_SETTING_RDCSDC2;
    	HSSPI_RDCSDC3 = DEF_SETTING_RDCSDC3;
    	HSSPI_RDCSDC4 = DEF_SETTING_RDCSDC4;
    	HSSPI_RDCSDC5 = DEF_SETTING_RDCSDC5;
    	HSSPI_RDCSDC6 = DEF_SETTING_RDCSDC6;
    	HSSPI_RDCSDC7 = DEF_SETTING_RDCSDC7;
    }

	//setup command sequencer mode
	HSSPI_MCTRL = 0x00000023;
    Delayus(1);
	// wait for module to be enabled
	while( HSSPI_MCTRL_MES != 1 )
	{
	// do what has to be done while waiting
	}
}

unsigned long S25FL512S_ReadID(unsigned char ui8Slave)
{
	unsigned long dummy;
	dummy = dummy;

	// select slave
	HSSPI_DMPSEL_PSEL = (ui8Slave & 0x03);
	// configure transport protocol (TX only, single bit)
	HSSPI_DMTRP_TRP = 0x00; // TRP[3..2] = b'00 (TX + RX), TRP[1..0] = b'00 (single bit)
	// set the byte TX byte count
	HSSPI_DMBCS_DMBCC = 5;
	// start transmission
	HSSPI_DMSTART_START = 1;
	// write command into TXFIFO
	HSSPI_TXFIFO0 = RESCMD;
	HSSPI_TXFIFO1 = 0x00000000;
			
	while ( HSSPI_DMSTATUS_TXACTIVE == 1 || HSSPI_DMSTATUS_RXACTIVE == 1 )
	{
		// do what has to be done here
		WDTCPR1 = 0xa5;
	}
		
	dummy = HSSPI_RXFIFO0; // dummy read on the RX FIFO
	
	return HSSPI_RXFIFO0;
}

unsigned char S25FL512S_ReadStatusRegister1(unsigned char ui8Slave)
{
	unsigned char result;
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;
    
	// select slave
	HSSPI_DMPSEL_PSEL = (ui8Slave & 0x03);
	
	// configure transport protocol (TX only, single bit)
	HSSPI_DMTRP_TRP = 0x00; // TRP[3..2] = b'00 (TX + RX), TRP[1..0] = b'00 (single bit)

	// set the byte TX byte count
	HSSPI_DMBCS_DMBCC = 2;

	// start transmission
	HSSPI_DMSTART_START = 1;
	
	// write command into TXFIFO
	HSSPI_TXFIFO0 = RDSR1CMD;
			
	while ( HSSPI_DMSTATUS_TXACTIVE == 1 || HSSPI_DMSTATUS_RXACTIVE == 1 )
	{
		// do what has to be done here
		WDTCPR1 = 0xa5;
	}
			
	result = (unsigned char)((HSSPI_RXFIFO0 & 0x00FF0000) >> 16);
	
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;
	
	return result;
}

unsigned char S25FL512S_ReadStatusRegister2(unsigned char ui8Slave)
{
	unsigned char result;
	
	// select slave
	HSSPI_DMPSEL_PSEL = (ui8Slave & 0x03);
	
	// configure transport protocol (TX only, single bit)
	HSSPI_DMTRP_TRP = 0x00; // TRP[3..2] = b'00 (TX + RX), TRP[1..0] = b'00 (single bit)

	// set the byte TX byte count
	HSSPI_DMBCS_DMBCC = 2;

	// start transmission
	HSSPI_DMSTART_START = 1;
	
	// write command into TXFIFO
	HSSPI_TXFIFO0 = RDSR2CMD;
			
	while ( HSSPI_DMSTATUS_TXACTIVE == 1 || HSSPI_DMSTATUS_RXACTIVE == 1 )
	{
		// do what has to be done here
		WDTCPR1 = 0xa5;
	}
			
	result = (unsigned char)((HSSPI_RXFIFO0 & 0x00FF0000) >> 16);
	
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;
	
	return result;
}

unsigned char S25FL512S_ReadConfigurationRegister(unsigned char ui8Slave)
{
	unsigned char result;
	
	// select slave
	HSSPI_DMPSEL_PSEL = (ui8Slave & 0x03);
	
	// configure transport protocol (TX only, single bit)
	HSSPI_DMTRP_TRP = 0x00; // TRP[3..2] = b'00 (TX + RX), TRP[1..0] = b'00 (single bit)

	// set the byte TX byte count
	HSSPI_DMBCS_DMBCC = 2;

	// start transmission
	HSSPI_DMSTART_START = 1;
	
	// write command into TXFIFO
	HSSPI_TXFIFO0 = 0x35000000;
			
	while ( HSSPI_DMSTATUS_TXACTIVE == 1 || HSSPI_DMSTATUS_RXACTIVE == 1 )
	{
		// do what has to be done here
		WDTCPR1 = 0xa5;
	}
			
	result = (unsigned char)((HSSPI_RXFIFO0 & 0x00FF0000) >> 16);
	
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;
	
	return result;
}

void S25FL512S_SetWriteEnble(unsigned char ui8Slave)
{	
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;

	// select slave
	HSSPI_DMPSEL_PSEL = (ui8Slave & 0x03);
	
	// configure transport protocol (TX only, single bit)
	HSSPI_DMTRP_TRP = 0x00; // TRP[3..2] = b'00 (TX + RX), TRP[1..0] = b'00 (single bit)

	// set the byte TX byte count
	HSSPI_DMBCS_DMBCC = 1;

	// start transmission
	HSSPI_DMSTART_START = 1;
	
	// write command into TXFIFO
	HSSPI_TXFIFO0 = WRENCMD;
			
	while ( HSSPI_DMSTATUS_TXACTIVE == 1 || HSSPI_DMSTATUS_RXACTIVE == 1 )
	{
		// do what has to be done here
		WDTCPR1 = 0xa5;
	}
	
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;
}

void S25FL512S_ClearWriteEnble(unsigned char ui8Slave)
{
	// select slave
	HSSPI_DMPSEL_PSEL = (ui8Slave & 0x03);
	
	// configure transport protocol (TX only, single bit)
	HSSPI_DMTRP_TRP = 0x08; // TRP[3..2] = b'00 (TX + RX), TRP[1..0] = b'00 (single bit)

	// set the byte TX byte count
	HSSPI_DMBCS_DMBCC = 1;

	// start transmission
	HSSPI_DMSTART_START = 1;
	
	// write command into TXFIFO
	HSSPI_TXFIFO0 = WRDICMD;
			
	while ( HSSPI_DMSTATUS_TXACTIVE == 1 || HSSPI_DMSTATUS_RXACTIVE == 1 )
	{
		// do what has to be done here
		WDTCPR1 = 0xa5;
	}
	
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;	
}

void S25FL512S_ModeBitReset(unsigned char ui8Slave)
{
	// select slave
	HSSPI_DMPSEL_PSEL = (ui8Slave & 0x03);
	
	// configure transport protocol (TX only, single bit)
	HSSPI_DMTRP_TRP = 0x08; // TRP[3..2] = b'00 (TX + RX), TRP[1..0] = b'00 (single bit)

	// set the byte TX byte count
	HSSPI_DMBCS_DMBCC = 1;

	// start transmission
	HSSPI_DMSTART_START = 1;
	
	// write command into TXFIFO
	HSSPI_TXFIFO0 = MODEBITRESETCMD;
			
	while ( HSSPI_DMSTATUS_TXACTIVE == 1 || HSSPI_DMSTATUS_RXACTIVE == 1 )
	{
		// do what has to be done here
		WDTCPR1 = 0xa5;
	}
	
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;	
}


void S25FL512S_EnableQuadOutput(unsigned char ui8Slave)
{
	unsigned char u8Status;
	unsigned long data =0;
	// set write enable and wait until it is set
	PFR04_P1 = 1; // QSPI_SIO2
	PFR04_P2 = 1; // QSPI_SIO3
	S25FL512S_SetWriteEnble(ui8Slave);
	do
	{	
		u8Status = S25FL512S_ReadStatusRegister1(ui8Slave);
	}
	while ( ( u8Status & 0x02) != 0x02 );

	// select slave
	HSSPI_DMPSEL_PSEL = (ui8Slave & 0x03);
	
	// configure transport protocol (TX only, single bit)
	HSSPI_DMTRP_TRP = 0x00; // TRP[3..2] = b'00 (TX + RX), TRP[1..0] = b'00 (single bit)

    if((FlashMFandID&0x00FF0000U) == 0x00EF0000U)
    {//winbond
    	// set the byte TX byte count
    	HSSPI_DMBCS_DMBCC = 3;
    	// write command into TXFIFO
    	data = (0x01 << 24) | (0x00 << 16) | (0x02 << 8); 
    }
    else if((FlashMFandID&0x00FF0000U) == 0x009D0000U)
    {//ISSI
        PPCR04_P2 = 1; //QSPI_SIO3 pull up
        PPER04_P2 = 1; //QSPI_SIO3 pull enable
    	// set the byte TX byte count
    	HSSPI_DMBCS_DMBCC = 2;
    	// write command into TXFIFO
    	data = (0x01 << 24) | (0x40 << 16);
    }
    else if((FlashMFandID&0x00FF0000U) == 0x00C80000U)
    {//GD
    	// set the byte TX byte count
    	HSSPI_DMBCS_DMBCC = 3;
    	// write command into TXFIFO
    	data = (0x01 << 24) | (0x00 << 16) | (0x02 << 8); 
    }
    else if((FlashMFandID&0x00FF0000U) == 0x00010000U)
    {//cypress
    	// set the byte TX byte count
    	HSSPI_DMBCS_DMBCC = 3;
    	// write command into TXFIFO
    	data = (0x01 << 24) | (0x00 << 16) | (((DEF_LC <<6) | (0x02))<< 8);
    }
    else
    {
    	// set the byte TX byte count
    	HSSPI_DMBCS_DMBCC = 3;
    	// write command into TXFIFO
    	data = (0x01 << 24) | (0x00 << 16) | (0x02 << 8);    
    }
	// start transmission
	HSSPI_DMSTART_START = 1;
	HSSPI_TXFIFO0 = data;
    
	while ( HSSPI_DMSTATUS_TXACTIVE == 1 || HSSPI_DMSTATUS_RXACTIVE == 1 )
	{
		// do what has to be done here
		WDTCPR1 = 0xa5;
	}
	
	// wait until write is done
	do
	{
		u8Status = S25FL512S_ReadStatusRegister1(ui8Slave);
	}
	while ( ( u8Status & 0x01) == 0x01 );
	
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;
}

#define MAXCHUNKSIZE 128
void S25FL512S_WriteData(unsigned char ui8Slave, unsigned long u32StartAddress, unsigned char *u8Data, unsigned long u16NumberOfBytes)
{
	unsigned long dummy;
	volatile unsigned long readdummy;
	unsigned long u16RemainingByteCount = u16NumberOfBytes;
	unsigned long u16ChunkSize, u16BytesToWrite, u16Remainder;
	unsigned long u16LengthOfCommand = 5;
	//unsigned long u16CommandCount;
	unsigned long u16BlockCount;
	unsigned char u8Status;

	// select slave
	HSSPI_DMPSEL_PSEL = (ui8Slave & 0x03);
			
	// program data
	while( u16RemainingByteCount != 0 )
	{
		u16ChunkSize = 0;
		
		// set write enable
		S25FL512S_SetWriteEnble(ui8Slave);
		do
		{	
			u8Status = S25FL512S_ReadStatusRegister1(ui8Slave);
		}
		while ( ( u8Status & 0x02) != 0x02 );

	
		if( u16RemainingByteCount / MAXCHUNKSIZE > 0 )
		{
			u16BytesToWrite = u16LengthOfCommand + MAXCHUNKSIZE;
		}
		else
		{
			u16BytesToWrite = u16LengthOfCommand + u16RemainingByteCount;
		}
	
		HSSPI_DMTRP_TRP = 0x00; // TX-RX mode
		HSSPI_DMSTART_START = 1; // set CS low
	
		HSSPI_DMBCS_DMBCC = u16BytesToWrite; // write one byte (for the instruction) + 4 byte for address		
		HSSPI_TXFIFO0 = ( FOURPPCMD & 0xFF000000 ) | ( u32StartAddress >> 8 ); // this should generate one byte output with CS low afterwards

		// wait for TXFIFO written to device
		while ( HSSPI_DMSTATUS_TXLEVEL != 0 )
		{
			WDTCPR1 = 0xa5;
		}
		
		// dummy read to clear RXFIFO
		readdummy = HSSPI_RXFIFO0;	
		
		dummy = 0 | ( u32StartAddress << 24 );
		u16BlockCount = 1;
		while( u16BlockCount < 4 )
		{
			if ( u16RemainingByteCount > 0 )
			{
				dummy |= ((unsigned long)(*u8Data) << ( 24 - 8 * u16BlockCount ));
				u8Data++;
				u16RemainingByteCount--;
			}
			else
			{
				dummy |= ((unsigned long)(0xFF) << ( 24 - 8 * u16BlockCount ));
			}
			
			u16BlockCount++;
		}
		u16ChunkSize += 8;		
		HSSPI_TXFIFO1 = dummy;

		// wait for TXFIFO written to device
		while ( HSSPI_DMSTATUS_TXLEVEL != 0 )
		{
			WDTCPR1 = 0xa5;
		}
		
		// dummy read to clear RXFIFO
		readdummy = HSSPI_RXFIFO0;

		
		/* <--- command, address and maximum 3 data bytes written -> */				
						
		while( u16ChunkSize < u16BytesToWrite )
		{
			u16Remainder = u16BytesToWrite - u16ChunkSize; // needed to check how many bytes can be collected into a word
			if ( u16Remainder >= 4 ) 
			{
				dummy = 0 | ( *u8Data << 24 | *(u8Data + 1) << 16 | *(u8Data + 2) << 8 | *(u8Data + 3) );
							
				u8Data += 4;
				u16ChunkSize += 4;
				u16RemainingByteCount -= 4;
			}
			else
			{
				unsigned short u16Count = 0;
				dummy = 0;
				// fill last bytes
				while ( u16Remainder > 0 )
				{
					dummy |= (*u8Data) << (24 - 8 * u16Count);
					u8Data++;
					u16Remainder--;
					u16RemainingByteCount--;
					u16ChunkSize += 1;
				}
			}	
			
			HSSPI_TXFIFO0 = dummy;
			
			// wait for TXFIFO written to device
			while ( HSSPI_DMSTATUS_TXLEVEL != 0 )
			{
				WDTCPR1 = 0xa5;
			}
	
			// dummy read to clear RXFIFO
			readdummy = HSSPI_RXFIFO0;
		}
			
		// clean up after chunk is written
		HSSPI_FIFOCFG_TXFLSH = 1;
		HSSPI_FIFOCFG_RXFLSH = 1;
		HSSPI_DMBCS_DMBCC = 0x00000000;			
			
		// wait until write of chunk is done
		do
		{
			u8Status = S25FL512S_ReadStatusRegister1(0);
		}
		while ( ( u8Status & 0x01) == 0x01 );
		
		u32StartAddress += ( u16BytesToWrite - u16LengthOfCommand );
	}
			
	// clear write enable
	S25FL512S_ClearWriteEnble(ui8Slave);
	do
	{	
		u8Status = S25FL512S_ReadStatusRegister1(ui8Slave);
	}
	while ( ( u8Status & 0x02) != 0x00 );
	
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;
}

void S25FL512S_ReadData(unsigned char ui8Slave, unsigned long u32StartAddress, unsigned char *u8Data, unsigned short u16NumberOfBytes)
{
	unsigned short u16NumberOfReadBytes;
	unsigned short u16LengthOfCommand = 5;
	unsigned short u16CommandCount;

	// select slave
	HSSPI_DMPSEL_PSEL = (ui8Slave & 0x03);
		
	HSSPI_DMTRP_TRP = 0x00; // TX-RX-mode
	
	HSSPI_DMBCS_DMBCC = u16LengthOfCommand + u16NumberOfBytes; // write five byte (one for the instruction + 4 for the address)
	HSSPI_TXFIFO0 = ( FOURREADCMD & 0xFF000000 ) | ( u32StartAddress >> 8 ); // this should generate one byte output with CS low afterwards
	HSSPI_TXFIFO1 = 0 | ( u32StartAddress << 24 );
	
	HSSPI_DMSTART_START = 1; // set CS low
	
	// wait for TXFIFO written to device
	while ( HSSPI_DMSTATUS_TXLEVEL != 0 )
	{
		WDTCPR1 = 0xa5;
	}
	
	u16NumberOfReadBytes = 0;
	u16CommandCount = u16LengthOfCommand;
	while( u16NumberOfReadBytes < u16NumberOfBytes + u16LengthOfCommand )
	{
		unsigned long dummy;
		unsigned char u8BlockCount;
				
		HSSPI_TXFIFO0 = 0xFFFFFFFF;

		while( HSSPI_DMSTATUS_RXLEVEL == 0) // wait for data received in RXFIFO
		{
			WDTCPR1 = 0xa5;
		}
		
		do
		{
			dummy = HSSPI_RXFIFO0;
			u8BlockCount = 0;
		
			while( ( u8BlockCount < 4 ) && ( u16NumberOfReadBytes < ( u16NumberOfBytes + u16LengthOfCommand ) ) )
			{
				if ( u16CommandCount > 0 )
				{
					u16CommandCount--;
				}
				else
				{
					*u8Data = (unsigned char)(dummy >> (24 - 8 * u8BlockCount));
					u8Data++;
				}
				u8BlockCount++;
				u16NumberOfReadBytes++;
			}
		}
		while( HSSPI_DMSTATUS_RXLEVEL != 0);		
	}
	
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;
	
}

void S25FL512S_ChipErase(unsigned char u8Slave)
{
	unsigned char u8Status;
	
	// set write enable
	S25FL512S_SetWriteEnble(u8Slave);
	do
	{	
		u8Status = S25FL512S_ReadStatusRegister1(u8Slave);
	}
	while ( ( u8Status & 0x02) != 0x02 );

		// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;
	
	// select slave
	HSSPI_DMPSEL_PSEL = (u8Slave & 0x03);
		
	HSSPI_DMTRP_TRP = 0x08; // TX-only mode
	
	HSSPI_DMBCS_DMBCC = 1; // write one byte only
	
	HSSPI_TXFIFO0 = 0xC7000000;
	
	HSSPI_DMSTART_START = 1;
	
	// wait until chip erase is done
	do
	{
		u8Status = S25FL512S_ReadStatusRegister1(0);
	}
	while ( ( u8Status & 0x01) == 0x01 );

	// clear write enable	
	S25FL512S_ClearWriteEnble(u8Slave);
	do
	{	
		u8Status = S25FL512S_ReadStatusRegister1(u8Slave);
	}
	while ( ( u8Status & 0x02) != 0x00 );	
	
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;
}

void S25FL512S_SectorErase(unsigned char u8Slave, unsigned long u32SectorAddress)
{
	unsigned char u8Status;
	
	// set write enable
	S25FL512S_SetWriteEnble(u8Slave);
	do
	{	
		u8Status = S25FL512S_ReadStatusRegister1(u8Slave);
	}
	while ( ( u8Status & 0x02) != 0x02 );
	
	// select slave
	HSSPI_DMPSEL_PSEL = (u8Slave & 0x03);
		
	HSSPI_DMTRP_TRP = 0x08; // TX-only mode
	
	HSSPI_DMBCS_DMBCC = 5; // write one byte only
	
	HSSPI_TXFIFO0 = 0xDC000000 | (u32SectorAddress >> 8);
	HSSPI_TXFIFO1 = 0 | (u32SectorAddress << 24);
	
	HSSPI_DMSTART_START = 1;
	
	// wait until chip erase is done
	do
	{
		u8Status = S25FL512S_ReadStatusRegister1(0);
	}
	while ( ( u8Status & 0x01) == 0x01 );

	// clear write enable	
	S25FL512S_ClearWriteEnble(u8Slave);
	do
	{	
		u8Status = S25FL512S_ReadStatusRegister1(u8Slave);
	}
	while ( ( u8Status & 0x02) != 0x00 );	
	
	// clean up
	HSSPI_FIFOCFG_TXFLSH = 1;
	HSSPI_FIFOCFG_RXFLSH = 1;
	HSSPI_DMBCS_DMBCC = 0x00000000;
}

